package drds.server.route.parser.core.impl;

import java.util.List;

import drds.server.config.model.Schema;
import drds.server.config.model.Table;
import drds.server.route.Route;
import drds.server.route.parser.core.ShardInfoVisitor;
import drds.server.route.parser.core.TableColumnValueSetMap;
import drds.server.route.util.RouteUtil;
import drds.server.server.parser.ServerParse;
import drds.server.sqlparser.sqlobject.expression.Expression;
import drds.server.sqlparser.sqlobject.statement.Statement;
import drds.server.sqlparser.sqlobject.statement.insert.InsertStatement;

public class InsertParser extends DefaultParser {
	@Override
	public void visitorParse(//
			Route route,//
			Statement statement,//
			ShardInfoVisitor visitor) {//

	}

	/**
	 * 考虑因素：ischildtable、批量
	 */
	@Override
	public void statementParse(//
			Route route,//
			Schema schema,//
			Statement statement) {//
		InsertStatement insertStatement = (InsertStatement) statement;
		String tableName = insertStatement.getTableName().getName();

		parserResult.addTableName(tableName);

		Table table = schema.getTableMap().get(tableName);
		if (table == null) {
			throw new NullPointerException("table");
		}
		// childTable的insert直接在解析过程中完成路由
		if (table.isChildTable()) {
			parseERChildTable(route, schema, tableName, insertStatement);
			return;
		} else {
			String shardColumnName = table.getShardColumnName();
			if (shardColumnName != null) {
				if (insertStatement.getColumnNameList() == null || insertStatement.getColumnNameList().size() == 0) {
					throw new IllegalArgumentException("partition table, insert must provide ColumnList");
				}
				parserSingleInsert(route, schema, insertStatement, tableName, shardColumnName);//

			}
		}

	}

	/**
	 * 寻找rootKey的索引
	 * 
	 * @param columnNameList
	 * @param joinKey
	 * @return -1表示没找到，>=0表示找到了
	 */
	private int getRootKeyIndex(List<Expression> columnNameList, String joinKey) {
		int size = columnNameList.size();
		for (int i = 0; i < size; i++) {
			String columnName = columnNameList.get(i).toString();
			if (columnName.equals(joinKey)) {
				return i;
			}
		}
		return -1;
	}

	// ER表所有的子表都会保存ROOT父表的路由信息。那么则很快的路由计算。
	private Route parseERChildTable(//
			Route route,//
			Schema schema, //
			String tableName,//
			InsertStatement insertStatement//
	) {
		Table table = schema.getTableMap().get(tableName);
		String rootKeyName = table.getRootParentTableKeyColumnName();
		//
		int rootKeyNameIndex = getRootKeyIndex(insertStatement.getColumnNameList(), rootKeyName);
		if (rootKeyNameIndex == -1) {
			String msg = "rootKeyName:" + table.getJoinKeyColumnName() + " is not existed at " + insertStatement;
			LOGGER.error(msg);
			throw new IllegalArgumentException(msg);
		}

		String rootKeyNameValue = insertStatement.getValueList().getValueList().get(rootKeyNameIndex).toString();
		String sql = insertStatement.toString();
		// try to route by ER parent partion key

		/**
		 * ER子表插入需要带上父表所有的分表标记 root_key{a:$a,b:$b}
		 */
		Route $route = RouteUtil.routeByERRootKey(//
				route,//
				schema, //
				table,//
				sql,//
				ServerParse.INSERT, //
				rootKeyNameValue);//
		if ($route != null) {
			route.setFinishedRoute(true);
			return $route;
		} else {
			throw new IllegalArgumentException("没有找到切分数据库和相关的表的路由信息");
		}

	}

	/**
	 * <pre>
	 * 单条insert,支持单值key和两值key。
	 * 三值KEY的维护模型实在是太复杂了。
	 * 三维模型是用户 时间 地点，一般情况地点是没有必要的，
	 * 互联网企业基本上是基于网络的，且基于用户进行地域分区。
	 * 当然如果是基于地域进行三维分区，那么则用户和地域需要结合在一起。
	 * 如果是这样需要进行按照地域存储相关信息 但是这样会存在热点的问题，
	 * 直接采用用户结合地域标记即可。
	 * </pre>
	 */
	private void parserSingleInsert(Route route, Schema schema, InsertStatement insertStatement, String tableName, String shardColumnName) {
		boolean isFound = false;
		int size = insertStatement.getColumnNameList().size();
		for (int i = 0; i < size; i++) {
			// 找到分片字段
			if (shardColumnName.equals(insertStatement.getColumnNameList().get(i).toString())) {
				isFound = true;
				/**
				 * 获取条件值
				 */
				String columnName = insertStatement.getColumnNameList().get(i).toString();
				String columnValue = insertStatement.getValueList().getValueList().get(i).toString();

				TableColumnValueSetMap tableColumnValueSetMap = new TableColumnValueSetMap();
				tableColumnValueSetMap.addValue(tableName, columnName, columnValue);
				parserResult.addTableColumnValueSetMap(tableColumnValueSetMap);
				break;
			}
		}
		if (!isFound) {// 分片表的
			String msg = "can not find shardColumn:" + shardColumnName + "at table :" + tableName + "@" + insertStatement;
			LOGGER.error(msg);
			throw new NullPointerException(msg);
		}
	}

}
